/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2013 University of Colorado Denver
 * <min.choi@ucdenver.edu> <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#include <sxViewport.h>
#include <sxViewController.h>

const unsigned int DEFAULT_FPS = 60u;
const unsigned int DEFAULT_MAX_FPS = 1000u;

Sx::Interface::Viewport::Viewport(unsigned int fps) {
	this->realtime = false;
	this->userUpdates = true;
	this->warpCursor = false;
	if ( fps > DEFAULT_MAX_FPS ) this->framesPerSecond = DEFAULT_FPS;
	else this->framesPerSecond = fps;
}

Sx::Interface::Viewport::~Viewport() {
	/* this->controller NOT owned by this class */
}

bool Sx::Interface::Viewport::setController(const std::shared_ptr<ViewportController>& controller) {
	this->controller = controller;
	if ( this->controller == nullptr ) return false;
	return this->controller->acquire(this);
}

std::shared_ptr<Sx::Interface::ViewportController> Sx::Interface::Viewport::getController() const {
	return this->controller;
}

void Sx::Interface::Viewport::enableUserUpdates(bool b) {
	this->userUpdates = b;
}

void Sx::Interface::Viewport::disableUserUpdates() {
	this->userUpdates = false;
}

bool Sx::Interface::Viewport::setClearColor(const Color3f& color) {
	if ( this->controller == nullptr  ) return false;
	this->controller->setClearColor(color);
	return true;
}

bool Sx::Interface::Viewport::setClearColor(const Color4f& color) {
	if ( this->controller == nullptr  ) return false;
	this->controller->setClearColor(color);
	return true;
}

bool Sx::Interface::Viewport::setClearColor(float r, float g, float b) {
	if ( this->controller == nullptr  ) return false;
	this->controller->setClearColor(r, g, b);
	return true;
}

bool Sx::Interface::Viewport::setClearColor(float r, float g, float b, float a) {
	if ( this->controller == nullptr  ) return false;
	this->controller->setClearColor(r, g, b);
	return true;
}

void Sx::Interface::Viewport::setRealtime(bool realtime) {
	this->realtime = realtime;
}

bool Sx::Interface::Viewport::isRealtime() const {
	return this->realtime;
}

void Sx::Interface::Viewport::setWarpCursor(bool warp) {
	this->warpCursor = warp;
}

bool Sx::Interface::Viewport::hasCursorWarp() const {
	return this->warpCursor;
}

bool Sx::Interface::Viewport::setFramesPerSecond(unsigned int fps) {
	return false;
}

unsigned int Sx::Interface::Viewport::getFramesPerSecond() const {
	return this->framesPerSecond;
}

/*
 * Given the known size of this viewport and the cursors position, calculate
 * the displacement that should be applied to the cursor to have it stay within
 * the warp-bounds of this viewport.
 */
Sx::Graphics::Position2Di Sx::Interface::Viewport::calculateCursorWarpDisplacement(int cursorX, int cursorY) {
	Graphics::Position2Di displacement;
	displacement.setValues(0, 0);

	//--------------------------------------------------------------------------
	// If the cursor is passed the warp threshold on the left side - warp the
	// cursor to the right side (- 1 so its not in the threshold)
	//--------------------------------------------------------------------------
	if ( cursorX <= static_cast<int>(this->view.getLeft() + CURSOR_WARP_THRESHOLD) ) {
		displacement.setX(this->view.getWidth() - (2 * CURSOR_WARP_THRESHOLD) - 1);
	}

	//--------------------------------------------------------------------------
	// If the cursor is passed the warp threshold on the right side - warp the
	// cursor to the left side (+ 1 so its not in the threshold)
	//--------------------------------------------------------------------------
	if ( cursorX >= static_cast<int>(this->view.getRight() - CURSOR_WARP_THRESHOLD) ) {
		displacement.setX(-this->view.getWidth() + (2 * CURSOR_WARP_THRESHOLD) + 1);
	}

	//--------------------------------------------------------------------------
	// If the cursor is passed the warp threshold on the top - warp the cursor
	// to the bottom (- 1 so its not in the threshold)
	//--------------------------------------------------------------------------
	if ( cursorY <= static_cast<int>(this->view.getTop() + CURSOR_WARP_THRESHOLD) ) {
		displacement.setY(this->view.getHeight() - (2 * CURSOR_WARP_THRESHOLD) - 1);
	}

	//--------------------------------------------------------------------------
	// If the cursor is passed the warp threshold on the bottom - warp the
	// cursor to the top (+ 1 so its not in the threshold)
	//--------------------------------------------------------------------------
	if ( cursorY >= static_cast<int>(this->view.getBottom() - CURSOR_WARP_THRESHOLD) ) {
		displacement.setY(-this->view.getHeight() + (2 * CURSOR_WARP_THRESHOLD) + 1);
	}

	return displacement;
}

/* 
 * Returns the number of milli-seconds between each frame from the provided
 * frames-per-second.
 */
float Sx::Interface::Viewport::CalculateMillisecondIncrement(unsigned int fps) {
    return 1000.0f / static_cast<float>(fps);
}

/* 
 * Returns the number of nano-seconds between each frame from the provided
 * frames-per-second.
 */
float Sx::Interface::Viewport::CalculateNanosecondIncrement(unsigned int fps) {
	return 1000000000.0f / static_cast<float>(fps);
}
